package com.unitd.logistics.comm.utils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ResourceUtils;

import java.io.*;
import java.nio.file.FileSystemException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * 文件处理工具类
 *
 * @author Hudan
 * @version 1.0
 * @filename FileUtils.java
 * @copyright www.pactera.com
 * @date 2019-01-23
 */
@Slf4j
public class FileUtils extends org.apache.commons.io.FileUtils {

	/**
	 * 获取指定文件夹下的指定后缀文件列表
	 *
	 * @param results    结果文件列表
	 * @param directory  指定的需要筛查的文件夹
	 * @param extensions 文件后缀
	 * @return List
	 */
	public static List<File> listFiles(List<File> results, File directory, final String extensions) {

		File[] subFiles = directory.listFiles();
		for (File file : subFiles) {
			if (file.isDirectory()) {
				listFiles(results, file, extensions);
			} else if (file.getName().endsWith(extensions)) {
				results.add(file);
			}
		}
		return results;
	}

	/**
	 * 获取jar文件中文件列表
	 *
	 * @param jarFile    指定的jar文件
	 * @param extensions 文件后缀
	 * @return List
	 */
	public static List<String> listFiles(JarFile jarFile, String extensions) {
		if (jarFile == null || StringUtils.isEmpty(extensions))
			return null;

		List<String> files = new ArrayList<>();

		Enumeration<JarEntry> entries = jarFile.entries();
		while (entries.hasMoreElements()) {
			JarEntry entry = entries.nextElement();
			String name = entry.getName();

			if (name.endsWith(extensions)) {
				files.add(name);
			}
		}
		return files;
	}

	/**
	 * 获取项目的相对路径下文件的绝对路径
	 *
	 * @param parentDir 目标文件的父目录
	 *                  (例如说:工程的目录下,有lib与bin和conf目录,那么程序运行于lib与bin,那么需要的配置文件却是conf里面,则需要找到该配置文件的绝对路径)
	 * @param fileName  文件名
	 * @return 一个绝对路径
	 */
	public static String getPath(String parentDir, String fileName) {

		String path;
		String userdir = System.getProperty("user.dir");
		String userdirName = new File(userdir).getName();

		if (userdirName.equalsIgnoreCase("lib")
				|| userdirName.equalsIgnoreCase("bin")) {

			File newf = new File(userdir);
			File newp = new File(newf.getParent());

			if (fileName.trim().equals("")) {
				path = newp.getPath() + File.separator + parentDir;
			} else {
				path = newp.getPath() + File.separator + parentDir + File.separator + fileName;
			}
		} else {
			if (fileName.trim().equals("")) {
				path = userdir + File.separator + parentDir;
			} else {
				path = userdir + File.separator + parentDir + File.separator + fileName;
			}
		}
		return path;
	}

	/**
	 * 获取文件后缀名(不带点)
	 *
	 * @param fileName 文件名
	 * @return String
	 */
	public static String getFileExt(String fileName) {
		if (StringUtils.isBlank(fileName) || !fileName.contains(".")) {
			return "";
		} else {
			return fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase(); // 不带最后的点
		}
	}

	/**
	 * 压缩文件(防止中文乱码,使用apache的ZipOutputStream包)
	 *
	 * @param strs     需要压缩的文件数组
	 * @param zipname  压缩文件名称
	 * @param temppath 临时目录
	 */
	private static void writeZip(String[] strs, String zipname, String temppath) throws IOException {
		String[] files = strs;
		OutputStream os = new BufferedOutputStream(new FileOutputStream(temppath + "//" + zipname));
		ZipOutputStream zos = new ZipOutputStream(os);
		byte[] buf = new byte[8192];
		int len;
		for (int i = 0; i < files.length; i++) {
			File file = new File(files[i]);
			if (!file.isFile()) {
				continue;
			}

			ZipEntry ze = new ZipEntry(file.getName());
			zos.putNextEntry(ze);
			BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
			while ((len = bis.read(buf)) > 0) {
				zos.write(buf, 0, len);
			}
			zos.closeEntry();
			bis.close();
		}
		for (int i = 0; i < files.length; i++) {
			File file = new File(files[i]);
			file.delete();
		}
		zos.closeEntry();
		zos.close();
		os.close();
	}

	/**
	 * 删除单个文件
	 *
	 * @param fileName 被删除的文件名
	 * @return 如果删除成功，则返回true，否则返回false
	 */
	public static boolean deleteFile(String fileName) {
		File file = new File(fileName);
		if (file.exists() && file.isFile()) {
			if (file.delete()) {
				log.debug("删除文件 " + fileName + " 成功!");
				return true;
			} else {
				log.debug("删除文件 " + fileName + " 失败!");
				return false;
			}
		} else {
			log.debug(fileName + " 文件不存在!");
			return true;
		}
	}

	/**
	 * 删除目录及目录下的文件
	 *
	 * @param dirName 被删除的目录所在的文件路径
	 * @return 如果目录删除成功，则返回true，否则返回false
	 */
	public static boolean deleteDirectory(String dirName) {
		String dirNames = dirName;
		if (!dirNames.endsWith(File.separator)) {
			dirNames = dirNames + File.separator;
		}
		File dirFile = new File(dirNames);
		if (!dirFile.exists() || !dirFile.isDirectory()) {
			log.debug(dirNames + " 目录不存在!");
			return true;
		}

		boolean flag = true;
		// 列出全部文件及子目录
		File[] files = dirFile.listFiles();
		for (int i = 0; i < files.length; i++) {
			// 删除子文件
			if (files[i].isFile()) {
				flag = FileUtils.deleteFile(files[i].getAbsolutePath());
				// 如果删除文件失败，则退出循环
				if (!flag) {
					break;
				}
			}
			// 删除子目录
			else if (files[i].isDirectory()) {
				flag = FileUtils.deleteDirectory(files[i]
						.getAbsolutePath());
				// 如果删除子目录失败，则退出循环
				if (!flag) {
					break;
				}
			}
		}

		if (!flag) {
			log.debug("删除目录失败!");
			return false;
		}
		// 删除当前目录
		if (dirFile.delete()) {
			log.debug("删除目录 " + dirName + " 成功!");
			return true;
		} else {
			log.debug("删除目录 " + dirName + " 失败!");
			return false;
		}

	}

	/**
	 * 创建单个文件
	 *
	 * @param descFileName 文件名，包含路径
	 * @return 如果创建成功，则返回true，否则返回false
	 */
	public static boolean createFile(String descFileName) {
		File file = new File(descFileName);
		if (file.exists()) {
			log.debug("文件 " + descFileName + " 已存在!");
			return false;
		}
		if (descFileName.endsWith(File.separator)) {
			log.debug(descFileName + " 为目录，不能创建目录!");
			return false;
		}
		if (!file.getParentFile().exists()) {
			// 如果文件所在的目录不存在，则创建目录
			if (!file.getParentFile().mkdirs()) {
				log.debug("创建文件所在的目录失败!");
				return false;
			}
		}

		// 创建文件
		try {
			if (file.createNewFile()) {
				log.debug(descFileName + " 文件创建成功!");
				return true;
			} else {
				log.debug(descFileName + " 文件创建失败!");
				return false;
			}
		} catch (Exception e) {
			e.printStackTrace();
			log.debug(descFileName + " 文件创建失败!");
			return false;
		}
	}

	/**
	 * 创建目录
	 *
	 * @param descDirName 目录名,包含路径
	 * @return 如果创建成功，则返回true，否则返回false
	 */
	public static boolean createDirectory(String descDirName) {
		String descDirNames = descDirName;
		if (!descDirNames.endsWith(File.separator)) {
			descDirNames = descDirNames + File.separator;
		}
		File descDir = new File(descDirNames);
		if (descDir.exists()) {
			log.debug("目录 " + descDirNames + " 已存在!");
			return false;
		}
		// 创建目录
		if (descDir.mkdirs()) {
			log.debug("目录 " + descDirNames + " 创建成功!");
			return true;
		} else {
			log.debug("目录 " + descDirNames + " 创建失败!");
			return false;
		}

	}

	/**
	 * 写入文件
	 *
	 * @param fileName 要写入的文件名称
	 * @param content  要写入文件的内容
	 * @param append   是否往文件默认写入
	 */
	public static void writeToFile(String fileName, String content, boolean append) {
		writeToFile(fileName, content, "UTF-8", append);
	}

	/**
	 * 写入文件
	 *
	 * @param fileName 要写入的文件名称
	 * @param content  要写入文件的内容
	 * @param encoding 编码格式
	 * @param append   是否往文件默认写入
	 */
	public static void writeToFile(String fileName, String content, String encoding, boolean append) {
		try {
			write(new File(fileName), content, encoding, append);
			log.debug("文件 " + fileName + " 写入成功!");
		} catch (IOException e) {
			log.debug("文件 " + fileName + " 写入失败! " + e.getMessage());
		}
	}


	/**
	 * 获取文件分隔符
	 *
	 * @return
	 */
	public static String getFileSplit() {
		return System.getProperty("file.separator");
	}

	public static void copyFile(File source, File dest)
			throws FileSystemException {
		try {
			if (!source.exists()) {
				throw new FileSystemException("源文件不存在");
			}
			if (!source.isFile()) {
				throw new FileSystemException("源文件是目录");
			}

			if (dest.exists()) {
				dest.delete();
			}
			if (!dest.createNewFile()) {
				throw new FileSystemException("不能建立文件:" + dest);
			}
		} catch (Exception e) {
			throw new FileSystemException(e.getMessage());
		}
		try {
			FileInputStream sourceStream = new FileInputStream(source);
			FileOutputStream destStream = new FileOutputStream(dest);
			byte[] buf = new byte[1024];
			int len = 0;
			len = sourceStream.read(buf);
			while (len > 0) {
				destStream.write(buf, 0, len);
				len = sourceStream.read(buf);
			}
			sourceStream.close();
			destStream.flush();
			destStream.close();
		} catch (Exception e) {
			throw new FileSystemException(e.getMessage());
		}
	}

	/**
	 * 创建对应的目录
	 *
	 * @param path 文件路径
	 * @return boolean
	 */
	public static boolean createPath(File path) {
		if (!path.exists()) {
			return path.mkdir();
		}
		return true;
	}

	/**
	 * Springboot项目中获取当前web的根目录
	 *
	 * @return web应用根目录
	 */
	public static String getJarRootPath() {
		try {
			String path = ResourceUtils.getURL("classpath:").getPath();
			//=> file:/root/tmp/demo-springboot-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/
			log.debug("ResourceUtils.getURL(\"classpath:\").getPath() -> " + path);

			//创建File时会自动处理前缀和jar包路径问题  => /root/tmp
			File rootFile = new File(path);
			if (!rootFile.exists()) {
				log.info("根目录不存在, 重新创建");
				rootFile = new File("");
				log.info("重新创建的根目录: " + rootFile.getAbsolutePath());
			}
			// 获取的字符串末尾没有分隔符 /
			log.debug("项目根目录: " + rootFile.getAbsolutePath());
			return rootFile.getAbsolutePath();
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
}